#include "Stdafx.h"

#include "DbReader.h"
#include "Index.h"
#include "Progress.h"
#include "Terminate.h"
#include <streambuf>

namespace DbReader
{

#define SKIPS 58        // the number of skips.
#define MAXSKIP 10000   // the largest skip value

int skip[SKIPS]={5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,36,40,44,48,52,56,60,70,80,90,100,150,200,250,300,400,500,650,800,1000,1200,1400,1600,2000,2400,3200,4000,5000,7500,MAXSKIP};
int skipDTC[SKIPS] = {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33,34,35,36,37,38,39,40,45,50,55,60,65,70,75,80,85,90,100,120,140,160,180,200,300,400,500,600,MAXSKIP};


// compress database dir
wchar_t szReaderDatabaseDir[PATH_MAX];

// subdatabase record
cprsubdb cprsubdatabase[MAXPIECE+1][MAXPIECE+1][MAXPIECE+1][MAXPIECE+1][7][7][2]; // db subslice info

// dtc databases
dtcdatabase dtcsubdatabase[MAXPIECE+1][MAXPIECE+1][MAXPIECE+1][MAXPIECE+1][7][7][2];

// the cache
unsigned char *CacheBaseAddress; // allocate cache memory to this pointer
unsigned char **BlockPointer;	// pointers to the memory address of block #i, using unique block id i.
										// watch out for 32/64-bit compatibility problem
unsigned char *DTCCacheBaseAddress;
unsigned char **DTCBlockPointer;

int CacheSize; // the size of cache
int iNumberOfBlocks;

// a doubly linked list for LRU caching
struct bi
{
	int64 uniqueid; // which block is in this
	int64 next;     // which is the index of the next blockinfo in the linked list
	int64 prev;     // which is the index of the last blockinfo in the linked list
} *BlockInfo;

bi* DTCBlockInfo;

int64 Head,Tail; // array index of head and tail of the linked list.
int64 DTCHead, DTCTail;

// array for decoding
int RunLength[256]; // holds the run length for every byte
int Value[256];		// holds the value for every byte

// number of db buffers
int maxblocknum;
int maxblocknumDTC;
// max number of idx buffers
int maxidx;
int maxpieces;
int maxpiece;

#define MAXFP 50
FILE *dbfp[MAXFP]; // file pointers to db2...dbn - always open.
FILE *dtcdbfp[MAXFP]; // file pointers to db2...dbn - always open.


void SetDatabaseReaderDirectory(const wchar_t* lpDir)
{
	wcscpy_s(szReaderDatabaseDir, PATH_MAX, lpDir);
}

void SetReaderDatabaseName(wchar_t *dbname, int numPieces, bool index, bool dtc)
{
	wcscpy_s(dbname, PATH_MAX, szReaderDatabaseDir);
	int len = (int)wcslen(dbname);
	if(dbname[len - 1] != L'\\')
	{
		wcscat_s(dbname, PATH_MAX, L"\\");
	}

	wchar_t szFileName[PATH_MAX];

	if(dtc == true)
	{
		if(index == true)
			swprintf_s(szFileName, PATH_MAX,L"db%i.dtc.idx", numPieces);
		else
			swprintf_s(szFileName, PATH_MAX, L"db%i.dtc.cpr", numPieces);
	}
	else
	{
		if(index == true)
			swprintf_s(szFileName, PATH_MAX,L"db%i.idx", numPieces);
		else
			swprintf_s(szFileName, PATH_MAX, L"db%i.cpr", numPieces);
	}

	wcscat_s(dbname, PATH_MAX, szFileName);
}

template <typename char_type>
struct istreambuf : public std::basic_streambuf<char_type, std::char_traits<char_type> >
{
    istreambuf(char_type* buffer, std::streamsize bufferLength)
    {
        // set the "put" pointer the start of the buffer and record it's length.
		setg(buffer, buffer, buffer + bufferLength);
        setp(buffer, buffer + bufferLength);
    }
};


int ParseDtcIndexFile(wchar_t* idxFilename, int blockoffset, int fpcount)
{
	char c,colorchar;
	int bm,bk,wm,wk,bmrank,wmrank,color;
	int startbyte;
	int idx[MAXIDX8];
	int num=0;
	int firstblock = 0;
	int stat,stat0;
	dtcdb *dbpointer;
	const int iMaxBuffer = 300;
	char szBuffer[iMaxBuffer];

	std::ifstream ifs(idxFilename, std::ios::binary);
	if(ifs.bad())
		return -1;

	int64 fsize = ifs.tellg();
	ifs.seekg( 0, std::ios::end );
	fsize = ifs.tellg() - fsize;
	ifs.seekg(0, std::ios::beg);

	char* pMemoryBuffer = new char[(size_t)fsize];
	ifs.read(pMemoryBuffer, fsize);

	istreambuf<char> istreamBuffer(pMemoryBuffer, fsize);
	std::istream sStream(&istreamBuffer);
	//sStream.seekg(0, std::ios::beg);

	while (1) 
	{	
		// At this point it has to be a BASE line or else end of file. 
		if(sStream.eof())
			break;
		sStream.getline(szBuffer, iMaxBuffer);
		stat = sscanf_s(szBuffer, " BASE%i,%i,%i,%i,%i,%i,%c:",
				&wm, &wk, &bm, &bk, &wmrank, &bmrank, &colorchar);
		
		// Done if end-of-file reached. 
		if (stat <= 0)
			break;
		else if (stat < 7) 
		{
			return -2;
		}
		
		// Decode the color. 
		if (colorchar == 'b')
			color = BLACK;
		else
			color = WHITE;


		// Get the rest of the line.  It could be a n/n, or it could just
		// be a single character that is '+', '=', or '-'.
		char* s = szBuffer;
		while(*s != ':') s++;
		s++;

		if (isdigit(*s)) 
		{
			stat0 = sscanf_s(s, "%d/%d", &firstblock, &startbyte);
			if (stat0 < 2) 
			{
				stat = sscanf_s(s, "%c", &c);
				if (stat < 1) 
				{
					return -2;
				}
			}

			dbpointer = &dtcsubdatabase[wm][wk][bm][bk][wmrank][bmrank][color];
			dbpointer->firstblock = firstblock;		// which block in db. 
			dbpointer->blockoffset = blockoffset;	// which block overall. 
			dbpointer->ispresent = 1;				// watch out: this should be initialized to 0 for all!
			dbpointer->startbyte = startbyte;
			dbpointer->fp = fpcount;

			// now, we got the first line, maybe there are more... 
			// count the number of blocks in the db.
			num = 1;
			idx[0] = 0;		// first block is index 0. 
			while (isdigit(sStream.peek()))
			{
				sStream.getline(szBuffer, iMaxBuffer);
				sscanf_s(szBuffer, "%d", idx + num);

				num++;

				// Check for too many indices.
				if (num == MAXIDX8) 
				{
					return -2;
				}
			} // while

			// We stopped reading numbers. 
			dbpointer->numberofblocks = num;
			dbpointer->idx = new int[num];

			iNumberOfBlocks += num;

			memcpy(dbpointer->idx, idx, num * sizeof(int));
		}
	}

	// fclose(fp);

	return num+firstblock;
}


int ParseIndexFile(wchar_t* idxFilename, int blockoffset, int fpcount)
{
	FILE *fp;
	char c,colorchar;
	int bm,bk,wm,wk,bmrank,wmrank,color;
	int singlevalue,startbyte;
	int idx[MAXIDX8];
	int num=0;
	int firstblock = 0;
	int stat,stat0;
	cprsubdb *dbpointer;

	// open the file
	_wfopen_s(&fp, idxFilename, L"r");
	if(fp == NULL)
		return -1;

	while (1) 
	{
		// At this point it has to be a BASE line or else end of file. 
		stat = fscanf_s(fp, " BASE%i,%i,%i,%i,%i,%i,%c:",
				&wm, &wk, &bm, &bk, &wmrank, &bmrank, &colorchar);
		
		// Done if end-of-file reached. 
		if (stat <= 0)
			break;
		else if (stat < 7) 
		{
			return -2;
		}
		
		// Decode the color. 
		if (colorchar == 'b')
			color = BLACK;
		else
			color = WHITE;


		// Get the rest of the line.  It could be a n/n, or it could just
		// be a single character that is '+', '=', or '-'.
		stat = fgetc(fp);
		if (isdigit(stat)) 
		{
			ungetc(stat, fp);
			stat0 = fscanf_s(fp, "%d/%d", &firstblock, &startbyte);
			if (stat0 < 2) 
			{
				stat = fscanf_s(fp, "%c", &c);
				if (stat < 1) 
				{
					return -2;
				}
			}

			dbpointer = &cprsubdatabase[wm][wk][bm][bk][wmrank][bmrank][color];
			dbpointer->firstblock = firstblock;		// which block in db. 
			dbpointer->blockoffset = blockoffset;	// which block overall. 
			dbpointer->ispresent = 1;				// watch out: this should be initialized to 0 for all!
			dbpointer->value = 0;
			dbpointer->startbyte = startbyte;
			dbpointer->fp = fpcount;

			// now, we got the first line, maybe there are more... 
			// count the number of blocks in the db.
			num = 1;
			idx[0] = 0;		// first block is index 0. 
			while (fscanf_s(fp, "%d", idx + num) == 1) 
			{
				num++;

				// Check for too many indices.
				if (num == MAXIDX8) 
				{
					return -2;
				}
			} // while

			// We stopped reading numbers. 
			dbpointer->numberofblocks = num;
			dbpointer->idx = new int[num];

			iNumberOfBlocks += num;

			memcpy(dbpointer->idx, idx, num * sizeof(int));
		}
		else 
		{
			switch (stat) {
			case '+':
				singlevalue = WIN;
				break;
			case '=':
				singlevalue = DRAW;
				break;
			case '-':
				singlevalue = LOSS;
				break;
			default:
				return(0);
			}
			dbpointer = &cprsubdatabase[wm][wk][bm][bk][wmrank][bmrank][color];
			dbpointer->blockoffset = 0;
			dbpointer->firstblock = 0;
			dbpointer->idx = NULL;
			dbpointer->ispresent = 1;
			dbpointer->numberofblocks = 0;
			dbpointer->value = singlevalue;
			dbpointer->fp = fpcount;
		}
	}

	fclose(fp);

	return num+firstblock;
}


int PreloadDatabase(SEngineData* ed)
{
	// preloads the entire db or until the cache is full.
	FILE *fp;
	int n;
	unsigned char *diskblock;
	int cachepointer = 0;
	int DTCcachepointer = 0;
	wchar_t dbname[PATH_MAX];
	int blockoffset = 0;
	int autoloadnum = 0;
	int uniqueid = 0;

	int maxprogress = min(CacheSize * 2, iNumberOfBlocks);


	ENGINE_PROGRESS_DECLARE();

	ENGINE_PROGRESS_INIT(3, maxprogress);

	int progress = 0;

	for(n=2;n<MAXPIECES;n++)
	{
		SetReaderDatabaseName(dbname, n, false, false);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp == NULL)
			break;

		while(!feof(fp))
		{
			// get a memory address to write block to:
			diskblock = CacheBaseAddress + 1024*cachepointer;
			// and save it in the blockpointer array
			BlockPointer[cachepointer] = diskblock;
			// cacheindex array tells which block id is at a certain place in cache - must update it
			BlockInfo[cachepointer].uniqueid = cachepointer; //change to uniqueid
			// say what we're doing
			
			// this was weird: fread statement was at the end of this loop before july 30 2007 - 
			// meaning that the last block of the cache was never read into memory?
			fread(diskblock,1024,1,fp);

			cachepointer++;
			progress++;

			ENGINE_PROGRESS_REPORT(progress, ed);

			if( (progress & 0x3FF) == 0 )
				CHECK_TERMINATE(ed);

			// if we have preloaded the entire cache. no use going on.
			if(cachepointer >= CacheSize)
				return 1; 	
		} // while

		fclose(fp);
		// if we get to the autoload limit, we remember the block number up to which 
		// blocks will remain permanently in memory.

		SetReaderDatabaseName(dbname, n, false, true);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp == NULL)
			break;

		while(!feof(fp))
		{
			// get a memory address to write block to:
			diskblock = DTCCacheBaseAddress + 1024*DTCcachepointer;
			// and save it in the blockpointer array
			DTCBlockPointer[DTCcachepointer] = diskblock;
			// cacheindex array tells which block id is at a certain place in cache - must update it
			DTCBlockInfo[DTCcachepointer].uniqueid = DTCcachepointer; //change to uniqueid
			// say what we're doing
			
			// this was weird: fread statement was at the end of this loop before july 30 2007 - 
			// meaning that the last block of the cache was never read into memory?
			fread(diskblock,1024,1,fp);

			DTCcachepointer++;

			progress++;
			ENGINE_PROGRESS_REPORT(progress, ed);

			if( (progress & 0x3FF) == 0 )
				CHECK_TERMINATE(ed);

			// if we have preloaded the entire cache. no use going on.
			if(DTCcachepointer >= CacheSize)
				return 1; 	
		} // while

		fclose(fp);


		// if we get to the autoload limit, we remember the block number up to which 
		// blocks will remain permanently in memory.
		if(n==AUTOLOADSIZE)
			autoloadnum = cachepointer;

	} // for

	return autoloadnum;
}


int ReaderDbInit(int suggestedMB, SEngineData* ed)
{
	FILE *fp;
	wchar_t dbname[PATH_MAX];
	int i,n;
	int singlevalue=0;
	int blockoffset = 0;
	int dtcblockoffset = 0;
	int autoloadnum = 0;
	int fpcount = 0;
	int pifreturnvalue;
	int pieces=0;
	uint32 memsize;

	InitBool();
	InitBinomCoef();

	// initialize runlength and lookup array 
	for(i=0;i<81;i++)
		RunLength[i]=4;

	for(i=81;i<256;i++)
	{
		RunLength[i]= skip[(i-81)%SKIPS];
		Value[i]= ((i-81)/SKIPS);
	}

	// set the ispresent flag to zero for all databases, because it later gets set to 1, and
	// it is not quite clear that this will not accidentally be one!
	memset(cprsubdatabase, 0, (MAXPIECE+1)*(MAXPIECE+1)*(MAXPIECE+1)*(MAXPIECE+1)*98*sizeof(cprsubdb));
	memset(dtcsubdatabase, 0, (MAXPIECE+1)*(MAXPIECE+1)*(MAXPIECE+1)*(MAXPIECE+1)*98*sizeof(dtcdb));
	
	// detect largest present database and put the number of pieces in variable pieces:
	for(n=2; n<MAXPIECES; n++)
	{
		SetReaderDatabaseName(dbname, n, true, false);
		_wfopen_s(&fp, dbname, L"rb");
		if(fp)
		{
			pieces=n;
			fclose(fp);
		}
		else
			break;
    }

	// what the hell is this good for
	// assign values according to pieces"
	switch(pieces)
	{
		case 2:
			;
		case 3:
			;
		case 4:
			maxidx = MAXIDX4;
			maxblocknum = BLOCKNUM4;
			maxblocknumDTC = BLOCKNUM4_DTC;
			maxpieces = 4;
			maxpiece = 4;
			break;
		case 5:
			maxidx = MAXIDX5;
			maxblocknum = BLOCKNUM5;
			maxblocknumDTC = BLOCKNUM5_DTC;
			maxpieces = 5;
			maxpiece = 5;
			break;
		case 6:
			maxidx = MAXIDX6;
			maxblocknum = BLOCKNUM6;
			maxblocknumDTC = BLOCKNUM6_DTC;
			maxpieces = 6;
			maxpiece = 6;
			break;
		case 7:
			maxidx = MAXIDX7;
			maxblocknum = BLOCKNUM7;
			maxblocknumDTC = BLOCKNUM7_DTC;
			maxpieces = 7;
			maxpiece = 4;
			break;
		case 8:
			maxidx = MAXIDX8;
			maxblocknum = BLOCKNUM8;
			maxblocknumDTC = BLOCKNUM8_DTC;
			maxpieces = 8;
			maxpiece = 4;
			break;
	}
	
	// get the number of blocks in cache
	// division by two, half for DTC, half for positions
	CacheSize = 1024*( suggestedMB / 2); 
	iNumberOfBlocks = 0;

	// parse index files
	// blockoffset is the total number of blocks in all dbs belonging to an index file.
	blockoffset = 0;

	for(n=2;n<=maxpieces;n++)
	{
		SetReaderDatabaseName(dbname, n, true, false);

		// parse next index file
		pifreturnvalue = ParseIndexFile(dbname,blockoffset,fpcount);
		
		// if the index file is not present, or if an error occurs during parsing,
		// we do nothing. else, we update the max number of pieces.
		if(pifreturnvalue >= 0)
		{
			blockoffset += pifreturnvalue;
		}

		SetReaderDatabaseName(dbname, n, true, true);

		pifreturnvalue = ParseDtcIndexFile(dbname, dtcblockoffset, fpcount);
		if(pifreturnvalue >= 0)
		{
			dtcblockoffset += pifreturnvalue;
			fpcount++;
		}
	}
	
	// allocate memory for the cache
	memsize = CacheSize*1024;
	CacheBaseAddress = new unsigned char[memsize];
	DTCCacheBaseAddress = new unsigned char[memsize];

	
	// allocate memory for blockpointers
	// statement below was sizeof(int) which returned 4 even on the 64-bit version of windows?!
	memsize = maxblocknum*sizeof(BlockPointer);
	BlockPointer = (unsigned char**) new unsigned char [memsize];
	memsize = maxblocknumDTC*sizeof(BlockPointer);
	DTCBlockPointer = (unsigned char**) new unsigned char [memsize];

	// set blockpointers to NULL - this statement crashes on 64-bit machine
	for(i=0;i<maxblocknum;i++)
	{
		BlockPointer[i]=NULL;
	}

	for(i=0;i<maxblocknumDTC;i++)
	{
		DTCBlockPointer[i]=NULL;
	}
	
	// allocate memory for doubly linked list LRU
	memsize = CacheSize*sizeof(struct bi);
	BlockInfo =(bi*) new unsigned char[memsize];
	DTCBlockInfo =(bi*) new unsigned char[memsize];

	autoloadnum = PreloadDatabase(ed);


	// prepare arrays which describe linked list
	// todo: if we want to autoload all blocks with a blocknumber < X (which preload would return)
	// then we have to go from for i=0 to for i=X 
	autoloadnum=0;
	for(i=autoloadnum;i<CacheSize;i++)  //0
	{
		BlockInfo[i].next = i+1;
		BlockInfo[i].prev = i-1;
		BlockInfo[i].uniqueid = i;
		DTCBlockInfo[i].next = i+1;
		DTCBlockInfo[i].prev = i-1;
		DTCBlockInfo[i].uniqueid = i;
	}
	BlockInfo[CacheSize-1].next=-1;
	BlockInfo[autoloadnum].prev=-1; //0
	DTCBlockInfo[CacheSize-1].next=-1;
	DTCBlockInfo[autoloadnum].prev=-1; //0
	
	Head=autoloadnum; //0
	Tail = CacheSize-1;
	DTCHead=autoloadnum; //0
	DTCTail = CacheSize-1;

	// open file pointers for each db: keep open all the time.
	fpcount=0;
	for(n=2; n<MAXPIECES; n++)
	{
		SetReaderDatabaseName(dbname, n, false, false);
		_wfopen_s(&(dbfp[fpcount]), dbname, L"rb");


		SetReaderDatabaseName(dbname, n, false, true);
		_wfopen_s(&(dtcdbfp[fpcount]), dbname, L"rb");

		fpcount++;
	}
	
	return maxpieces;
}

//-------------------------------------------
// initialization below
void ReaderDbExit()
{
	delete[] CacheBaseAddress;
	delete[] BlockPointer;		
	delete[] BlockInfo;

	delete[] DTCCacheBaseAddress;
	delete[] DTCBlockPointer;		
	delete[] DTCBlockInfo;

	for(int i=0;i<MAXFP;i++)
	{
		if(dbfp[i] != NULL)
			fclose(dbfp[i]);	
	}
}

template<typename BB>
int ReaderDbLookup(BB *q, int cl, uint8 *pDTC)
{
	assert(false);
	return UNKNOWN;
}

template<>
int ReaderDbLookup<BitBoard>(BitBoard *q, int cl, uint8 *pDTC)
{
	int64 index;
	int bm, bk, wm, wk, bmrank=0, wmrank=0;
	*pDTC = 0;
	
	int i;
	int blocknumber;	
	int DTCblocknumber;
	int uniqueblockid;
	int DTCuniqueblockid;
	int reverse = 0;
	int returnvalue=UNKNOWN;
	int memsize = 0;
	unsigned char *diskblock;
	unsigned char *dtcdiskblock;
	int64 newhead,prev, next;
	int n;
	int dtcn;
	int *idx;
	int *dtcidx;
	unsigned char byte;
	BitBoard revpos;
	BitBoard p;
	cprsubdb *dbpointer;
	dtcdb *dtcpointer;
	int color = cl;

	p=*q;

	// calculate number of pieces
	wm = BitCount(p.wm);
	wk = BitCount(p.wk);
	bm = BitCount(p.bm);
	bk = BitCount(p.bk);

	if(wm + wk == 0)
	{
		if(color == WHITE)
			return LOSS;
		else
			return WIN;
	}

	if(bm + bk == 0)
	{
		if(color == WHITE)
			return WIN;
		else
			return LOSS;
	}

	if( (bm+wm+wk+bk>maxpieces) || (bm+bk>maxpiece) || (wm+wk>maxpiece))
		return UNKNOWN;	
	
	// decide maximum rank of men
	if(wm)
		wmrank = MSB(p.wm)/4;
	if(bm)
		bmrank = (31-LSB(p.bm))/4;	
	
	// if this BitBoard is dominated by the "wrong" side, we have to
	// reverse it!
	if (( ((bm+bk-wm-wk)<<16) + ((bk-wk)<<8) + ((bmrank-wmrank)<<4) + (1-cl)) > 0)
		reverse = 1;
	
	if (reverse)
	{
		// white is dominating this BitBoard, change colors
		revpos.wm = Revert(p.bm);
		revpos.wk = Revert(p.bk);
		revpos.bm = Revert(p.wm);
		revpos.bk = Revert(p.wk);
		color = color^1;
		p = revpos;
		
		reverse = bm;
		bm = wm;
		wm = reverse;
		
		reverse = bk;
		bk = wk;
		wk = reverse;
		
		reverse = bmrank;
		bmrank = wmrank;
		wmrank = reverse;
		
		reverse = 1;
	} // if


	// get pointer to db
	dbpointer = &cprsubdatabase[wm][wk][bm][bk][wmrank][bmrank][color];
	dtcpointer = &dtcsubdatabase[wm][wk][bm][bk][wmrank][bmrank][color];
	
	if(dbpointer->ispresent == 0 || (dbfp[dbpointer->fp] == NULL))
		return 123;
	if(dtcpointer->ispresent == 0 || (dtcdbfp[dtcpointer->fp] == NULL))
		return 125;

	
	bool oSingleValue = false;
	// check if the db contains only a single value
	if(dbpointer->value != UNKNOWN)
	{
		returnvalue =  dbpointer->value;
		oSingleValue = true;
	}

	// calculate index
	BitBoardToIndex(&p, &index);

	// now we know the index, and the database, so we look in the .idx file to find the
	// right memory block
	idx = dbpointer->idx;
	n= dbpointer->numberofblocks;
	dtcidx = dtcpointer->idx;
	dtcn= dtcpointer->numberofblocks;

	// now the array idx[] contains the index number at the start of every block.
	// we do a stupid linear search to find the block:
	blocknumber=0;
	while((blocknumber<n) && ((unsigned int)idx[blocknumber] <= index) )
		blocknumber++;
	// we overshot our target - blocknumber is now the first larger idx.
	blocknumber--;

	DTCblocknumber=0;
	while((DTCblocknumber<dtcn) && ((unsigned int)dtcidx[DTCblocknumber] <= index) )
		DTCblocknumber++;
	// we overshot our target - blocknumber is now the first larger idx.
	DTCblocknumber--;


	// we now have the blocknumber inside the database slice in which the BitBoard is located.
	// get the unique number which identifies this block
	uniqueblockid = dbpointer->blockoffset+
					dbpointer->firstblock +
					blocknumber;

	DTCuniqueblockid = dtcpointer->blockoffset+
					dtcpointer->firstblock +
					DTCblocknumber;


	if(oSingleValue == false)
	{
		// check if it is loaded:
		if(BlockPointer[uniqueblockid] != NULL)
		{
			diskblock = BlockPointer[uniqueblockid];
		
			// update LRU linked list
			// 1): which index does this block have in the array?
			newhead = (diskblock - CacheBaseAddress)/1024;
			if(newhead!=Head)
			{
				if(newhead == Tail)
				{
					// special case: we will have to reset tail too
					prev = BlockInfo[newhead].prev;
					BlockInfo[prev].next = -1;
					Tail = prev;
				
					BlockInfo[Head].prev = newhead;
					BlockInfo[newhead].next = Head;
					BlockInfo[newhead].prev = -1;
					Head = newhead;
				}
				else
				{
					// remove index "newhead" from doubly linked list:
					// relink previous and next block of newhead
					prev = BlockInfo[newhead].prev;
					next = BlockInfo[newhead].next;
					BlockInfo[prev].next = next;
					BlockInfo[next].prev = prev;
				
					// set info for newhead
					BlockInfo[newhead].prev = -1;
					BlockInfo[newhead].next = Head;

					// set info for head
					BlockInfo[Head].prev = newhead;
				
					Head = newhead;
				}
			}
		}
	
		else
		// we must load it
		{

			//------------------------------------------------
			// new LRU code
			// get address to load it to: write it into tail.

			diskblock = CacheBaseAddress + 1024*Tail;
			//printf("write to tail %i ",tail);
			// and save it in the blockpointer array
			BlockPointer[uniqueblockid]=diskblock;
			// reset blockpointer entry of whatever was there before
			// i use -1 for an empty cache block - nothing to unload...
			// blockpointer[blockid] points to the memory address
			if ( BlockInfo[Tail].uniqueid !=-1 )
			{
				BlockPointer[BlockInfo[Tail].uniqueid]=NULL;
			}
			// blockpointer array is updated

			// blockinfo array contains information on the linked list - must update it
			// steps: set current head's previous item
			//        get a new tail, and set it's next item to -1
			//        write new block into space where old tail was as new head
			//        set it's 3 items.

			newhead = Tail;
			Tail = BlockInfo[Tail].prev;
			BlockInfo[Tail].next = -1;
			BlockInfo[newhead].uniqueid=uniqueblockid;
			BlockInfo[newhead].next = Head;
			BlockInfo[newhead].prev = -1;
		
			BlockInfo[Head].prev = newhead;
			Head = newhead;

			// move to the disk block we are looking for
			//fseek(dbfp[bm+bk+wm+wk],blocknumber*1024,SEEK_SET);
			// now, we want to load a block. we have to seek the
			// right BitBoard - that is in this file, blocknumber+firstblock
			_fseeki64(dbfp[dbpointer->fp],(blocknumber+dbpointer->firstblock)*1024,SEEK_SET);
			// and read it
			//i = fread(diskblock,1024,1,dbfp[dbpointer->fp]);
			fread(diskblock,1024,1,dbfp[dbpointer->fp]);

		}
	}


	 /// DTC
	/////////////////////////////////////////////////////////////////////

	// check if it is loaded:
	if(DTCBlockPointer[DTCuniqueblockid] != NULL)
	{
		dtcdiskblock = DTCBlockPointer[DTCuniqueblockid];
		
		// update LRU linked list
		// 1): which index does this block have in the array?
		newhead = (dtcdiskblock - DTCCacheBaseAddress)/1024;
		if(newhead!=DTCHead)
		{
			if(newhead == DTCTail)
			{
				// special case: we will have to reset tail too
				prev = DTCBlockInfo[newhead].prev;
				DTCBlockInfo[prev].next = -1;
				DTCTail = prev;
				
				DTCBlockInfo[DTCHead].prev = newhead;
				DTCBlockInfo[newhead].next = DTCHead;
				DTCBlockInfo[newhead].prev = -1;
				DTCHead = newhead;
			}
			else
			{
				// remove index "newhead" from doubly linked list:
				// relink previous and next block of newhead
				prev = DTCBlockInfo[newhead].prev;
				next = DTCBlockInfo[newhead].next;
				DTCBlockInfo[prev].next = next;
				DTCBlockInfo[next].prev = prev;
				
				// set info for newhead
				DTCBlockInfo[newhead].prev = -1;
				DTCBlockInfo[newhead].next = DTCHead;

				// set info for head
				DTCBlockInfo[DTCHead].prev = newhead;
				
				DTCHead = newhead;
			}
		}
	}
	
	else
	// we must load it
	{

		//------------------------------------------------
		// new LRU code
		// get address to load it to: write it into tail.

		dtcdiskblock = DTCCacheBaseAddress + 1024*DTCTail;
		//printf("write to tail %i ",tail);
		// and save it in the blockpointer array
		DTCBlockPointer[DTCuniqueblockid]=dtcdiskblock;
		// reset blockpointer entry of whatever was there before
		// i use -1 for an empty cache block - nothing to unload...
		// blockpointer[blockid] points to the memory address
		if ( DTCBlockInfo[DTCTail].uniqueid !=-1 )
		{
			DTCBlockPointer[DTCBlockInfo[DTCTail].uniqueid]=NULL;
		}
		// blockpointer array is updated

		// blockinfo array contains information on the linked list - must update it
		// steps: set current head's previous item
		//        get a new tail, and set it's next item to -1
		//        write new block into space where old tail was as new head
		//        set it's 3 items.

		newhead = DTCTail;
		DTCTail = DTCBlockInfo[DTCTail].prev;
		DTCBlockInfo[DTCTail].next = -1;
		DTCBlockInfo[newhead].uniqueid=DTCuniqueblockid;
		DTCBlockInfo[newhead].next = DTCHead;
		DTCBlockInfo[newhead].prev = -1;
		
		DTCBlockInfo[DTCHead].prev = newhead;
		DTCHead = newhead;

		// move to the disk block we are looking for
		//fseek(dbfp[bm+bk+wm+wk],blocknumber*1024,SEEK_SET);
		// now, we want to load a block. we have to seek the
		// right BitBoard - that is in this file, blocknumber+firstblock
		_fseeki64(dtcdbfp[dbpointer->fp],(DTCblocknumber+dtcpointer->firstblock)*1024,SEEK_SET);
		// and read it
		//i = fread(diskblock,1024,1,dbfp[dbpointer->fp]);
		fread(dtcdiskblock,1024,1,dtcdbfp[dbpointer->fp]);
	}


	// the block we were looking for is now pointed to by diskblock
	// and it has been moved to the head of the linked list
	// now we decompress the memory block
	// get n, the offset index for this diskblock
	
	if(oSingleValue == false)
	{
		reverse=0;
		if(	dbpointer->numberofblocks>blocknumber+1)
			//we are not at the last block
		{
			if(idx[blocknumber+1]-index < index-idx[blocknumber])
				reverse=1;
		}

		if(reverse)
		{
			n=idx[blocknumber+1];
			i=1023;
			while((unsigned int)n>index /*&& i>=0*/)
			{
				n-=RunLength[diskblock[i]];
				i--;
			}
			i++;
		}
		else
		{
			n = idx[blocknumber];

			// and move through the bytes until we overshoot index
			// set the start byte in this block: if it's block 0 of a db, this can be !=0
			i=0;
			if(blocknumber == 0)
				i = dbpointer->startbyte;

			// LOOKUP LOOP - executes about 500 times on average
			// this could be speeded up by storing the index of the first 512 loops

			while((unsigned int)n<=index /*&& i<1024*/)
			{
				n+=RunLength[diskblock[i]];
				i++;
			}
		
			i--;
			n-=RunLength[diskblock[i]];
		}
	}

	 // DTC
	/////////////////////////////////
	dtcn = dtcidx[DTCblocknumber];

	// and move through the bytes until we overshoot index
	// set the start byte in this block: if it's block 0 of a db, this can be !=0
	int ii=0;
	if(DTCblocknumber == 0)
		ii = dtcpointer->startbyte;

	// LOOKUP LOOP - executes about 500 times on average
	// this could be speeded up by storing the index of the first 512 loops

	while((unsigned int)dtcn<=index /*&& i<1024*/)
	{
		if(dtcdiskblock[ii]>=81)
			dtcn += skipDTC[dtcdiskblock[ii] - 81], ii++;
		else
			dtcn++;
		ii++;
	}	
	ii--;

	
	
	if(oSingleValue == false)
	{
		// finally, we have found the byte which describes the BitBoard we
		// wish to look up. it is diskblock[i].
		//if(reverse)
		//	printf("\nbytenumbers reverse: %i nonreverse %i",bytenumber,i);	
		if(diskblock[i]>80)
		{
			// t'was a compressed byte - easy
			returnvalue = Value[diskblock[i]];
			byte = diskblock[i];
		}
		else
		{
			// an uncompressed byte
			byte = diskblock[i];
			i = (int)(index-n); // should be 0,1,2,3

			switch(i)
			{
				case 0:
					returnvalue = byte % 3;
					break;
				case 1:
					returnvalue = (byte/3) % 3;
					break;
				case 2:
					returnvalue = (byte/9) % 3;
					break;
				case 3:
					returnvalue = (byte/27) % 3;
					break;
			}
		}

		returnvalue++;
	}

	 // DTC
	/////////////////

	uint8 value = dtcdiskblock[ii];
	*pDTC = value;

	return returnvalue;
}

template<>
int ReaderDbLookup<BitBoard64>(BitBoard64 *q, int cl, uint8 *pDTC)
{
	return UNKNOWN;
}


}